import useVuelidate, {Validation, ValidationArgs} from '@vuelidate/core'
import {reactive, isReactive, Ref, unref, watch, isRef} from 'vue'
import _ from 'lodash'

/**
 * 集合验证的帮助套件
 */

// 集合验证返回定义
interface CollectionValSuite {
  // 增加一行明细，把lineData加入到collection末尾中，valObjs也同时增加一个到末尾
  addLine: (lineData: any) => void
  // 删除一行明细，从collection中移除掉一行，valObjs中也对应移除一行
  removeLine: (index: number) => void
  // 删除所有明细，从collection中移除所有行，valObjs中也对应移除所有行
  clearLines: () => void
  // 验证对象集合（响应式），与collection一一对应
  valObjs: Validation<any>[]
  // 触发验证
  touch: (...extraValObjs: Validation<any>[]) => void
  // 重置验证
  reset: (...extraValObjs: Validation<any>[]) => void
  // 验证方法，可以额外传入多个验证对象
  validate: (...extraValObjs: Validation<any>[]) => Promise<boolean>
}

/**
 * hook函数
 * @param collection 要验证的集合（响应式）
 * @param lineRule 行规则
 */
export function useCollectionValHelper(collection: any[] | Ref<any[]>, lineRule: ValidationArgs | ((line: any) => ValidationArgs)): CollectionValSuite {

  const valObjs: Validation[] = reactive([])

  const resetValObjs = function () {
    valObjs.splice(0, valObjs.length)
    _.forEach(unref(collection), function (item) {
      const rule = typeof (lineRule) === 'function' ? lineRule(item) : lineRule
      const valObj = useVuelidate(rule, item, {$autoDirty: true})
      valObjs.push(valObj.value)
    })
  }

  resetValObjs()

  if (isRef(collection)) {
    watch(collection, () => {
      resetValObjs()
    })
  }

  const addLine = (lineData: Record<string, Record<string, any>>) => {
    const lineDataReactive = isReactive(lineData) ? lineData : reactive(lineData)
    unref(collection).push(lineDataReactive)
    const rule = typeof (lineRule) === 'function' ? lineRule(lineDataReactive) : lineRule
    const valObj = useVuelidate(rule, lineDataReactive, {$autoDirty: true})
    valObjs.push(valObj.value)
  }

  const removeLine = (index: number) => {
    unref(collection).splice(index, 1)
    valObjs.splice(index, 1)
  }

  const clearLines = () => {
    const coll = unref(collection)
    coll.splice(0, coll.length)
    valObjs.splice(0, valObjs.length)
  }

  const touch = (...extraValObjs: Validation<any>[]) => {
    const valObjsToTouch = _.concat(valObjs, extraValObjs)
    _.forEach(valObjsToTouch, function (valObj) {
      valObj.$touch()
    })
  }

  const reset = (...extraValObjs: Validation<any>[]) => {
    const valObjsToReset = _.concat(valObjs, extraValObjs)
    _.forEach(valObjsToReset, function (valObj) {
      valObj.$reset()
    })
  }

  const validate = (...extraValObjs: Validation<any>[]): Promise<boolean> => {
    const valObjsToCheck = _.concat(valObjs, extraValObjs)
    const valResultPromises: Promise<boolean>[] = _.map(valObjsToCheck, l => l.$validate())
    return Promise.all(valResultPromises).then((results: boolean[]) => {
      return Promise.resolve(_.every(results, Boolean))
    })
  }

  return {
    valObjs,
    addLine,
    removeLine,
    clearLines,
    touch,
    reset,
    validate
  }
}
